/*
 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#ifndef APPLEKEXT
#include <newnfs/nfs/nfsport.h>

extern int nfsrv_useacl;
extern int nfsrv_debug;
#endif

static int nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
    enum vtype type, kauth_ace_rights_t *permp);
static void nfsacl_set_wellknown(int, guid_t *);

/*
 * Handle xdr for an ace.
 */
APPLESTATIC int
nfsrv_dissectace(struct nfsrv_descript *nd, kauth_ace_t acep,
    int *aceerrp, int *acesizep, NFSPROC_T *p)
{
	u_int32_t *tl;
	int len, gotid = 0, owner = 0, error = 0, aceerr = 0;
	u_char *name, namestr[NFSV4_SMALLSTR + 1];
	u_int32_t flag, mask, acetype;
	gid_t gid;
	uid_t uid;

	*aceerrp = 0;
	acep->ace_flags = 0;
	NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
	acetype = fxdr_unsigned(u_int32_t, *tl++);
	flag = fxdr_unsigned(u_int32_t, *tl++);
	mask = fxdr_unsigned(u_int32_t, *tl++);
	len = fxdr_unsigned(int, *tl);
	if (len < 0) {
		return (NFSERR_BADXDR);
	} else if (len == 0) {
		/* Netapp filers return a 0 length who for nil users */
		nfsacl_set_wellknown(KAUTH_WKG_NOBODY,
		    &acep->ace_applicable);
		acep->ace_rights = 0;
		if (acesizep)
			*acesizep = 4 * NFSX_UNSIGNED;
		return (0);
	}
	if (len > NFSV4_SMALLSTR)
		name = malloc(len + 1, M_NFSSTRING, M_WAITOK);
	else
		name = namestr;
	error = nfsrv_mtostr(nd, name, len);
	if (error) {
		if (len > NFSV4_SMALLSTR)
			free(name, M_NFSSTRING);
		return (error);
	}
	if (len == 6) {
		if (!NFSBCMP(name, "OWNER@", 6)) {
			nfsacl_set_wellknown(KAUTH_WKG_OWNER,
			    &acep->ace_applicable);
			owner = 1;
			gotid = 1;
		} else if (!NFSBCMP(name, "GROUP@", 6)) {
			nfsacl_set_wellknown(KAUTH_WKG_GROUP,
			    &acep->ace_applicable);
			gotid = 1;
		}
	} else if (len == 9 && !NFSBCMP(name, "EVERYONE@", 9)) {
		nfsacl_set_wellknown(KAUTH_WKG_EVERYBODY,
		    &acep->ace_applicable);
		gotid = 1;
	}
	if (gotid == 0) {
		if (flag & NFSV4ACE_IDENTIFIERGROUP) {
			aceerr = nfsv4_strtogid(name, len, &gid, p);
			if (aceerr == 0)
				aceerr = kauth_cred_gid2guid(gid,
				    &acep->ace_applicable);
		} else {
			aceerr = nfsv4_strtouid(name, len, &uid, p);
			if (aceerr == 0)
				aceerr = kauth_cred_uid2guid(uid,
				    &acep->ace_applicable);
		}
	}
	if (len > NFSV4_SMALLSTR)
		free(name, M_NFSSTRING);

	if (aceerr == 0) {
		/*
		 * Handle the flags.
		 */
		flag &= ~NFSV4ACE_IDENTIFIERGROUP;
		if (flag & NFSV4ACE_FILEINHERIT) {
			flag &= ~NFSV4ACE_FILEINHERIT;
			acep->ace_flags |= KAUTH_ACE_FILE_INHERIT;
		}
		if (flag & NFSV4ACE_DIRECTORYINHERIT) {
			flag &= ~NFSV4ACE_DIRECTORYINHERIT;
			acep->ace_flags |= KAUTH_ACE_DIRECTORY_INHERIT;
		}
		if (flag & NFSV4ACE_NOPROPAGATEINHERIT) {
			flag &= ~NFSV4ACE_NOPROPAGATEINHERIT;
			acep->ace_flags |= KAUTH_ACE_LIMIT_INHERIT;
		}
		if (flag & NFSV4ACE_INHERITONLY) {
			flag &= ~NFSV4ACE_INHERITONLY;
			acep->ace_flags |= KAUTH_ACE_ONLY_INHERIT;
		}
		if (flag & NFSV4ACE_SUCCESSFULACCESS) {
			flag &= ~NFSV4ACE_SUCCESSFULACCESS;
			acep->ace_flags |= KAUTH_ACE_SUCCESS;
		}
		if (flag & NFSV4ACE_FAILEDACCESS) {
			flag &= ~NFSV4ACE_FAILEDACCESS;
			acep->ace_flags |= KAUTH_ACE_FAILURE;
		}
		/*
		 * And set the kind.
		 */
		if (acetype == NFSV4ACE_ALLOWEDTYPE)
			acep->ace_flags |= KAUTH_ACE_PERMIT;
		else if (acetype == NFSV4ACE_DENIEDTYPE)
			acep->ace_flags |= KAUTH_ACE_DENY;
		else if (acetype == NFSV4ACE_AUDITTYPE)
			acep->ace_flags |= KAUTH_ACE_AUDIT;
		else if (acetype == NFSV4ACE_ALARMTYPE)
			acep->ace_flags |= KAUTH_ACE_ALARM;
		else
			aceerr = NFSERR_ATTRNOTSUPP;
	}

	/*
	 * Now, check for unsupported flag bits.
	 */
	if (aceerr == 0 && flag != 0)
		aceerr = NFSERR_ATTRNOTSUPP;

	/*
	 * And turn the mask into perm bits.
	 */
	if (aceerr == 0)
		aceerr = nfsrv_acemasktoperm(acetype, mask, owner, VREG,
		    &acep->ace_rights);
	*aceerrp = aceerr;
	if (acesizep)
		*acesizep = NFSM_RNDUP(len) + (4 * NFSX_UNSIGNED);
	return (0);
nfsmout:
	return (error);
}

/*
 * Set the rights.
 */
static int
nfsrv_acemasktoperm(u_int32_t acetype, u_int32_t mask, int owner,
    enum vtype type, kauth_ace_rights_t *permp)
{
	kauth_ace_rights_t perm = 0x0;

	if (mask & NFSV4ACE_READDATA) {
		mask &= ~NFSV4ACE_READDATA;
		perm |= KAUTH_VNODE_READ_DATA;
	}
	if (mask & NFSV4ACE_LISTDIRECTORY) {
		mask &= ~NFSV4ACE_LISTDIRECTORY;
		perm |= KAUTH_VNODE_LIST_DIRECTORY;
	}
	if (mask & NFSV4ACE_WRITEDATA) {
		mask &= ~NFSV4ACE_WRITEDATA;
		perm |= KAUTH_VNODE_WRITE_DATA;
	}
	if (mask & NFSV4ACE_ADDFILE) {
		mask &= ~NFSV4ACE_ADDFILE;
		perm |= KAUTH_VNODE_ADD_FILE;
	}
	if (mask & NFSV4ACE_APPENDDATA) {
		mask &= ~NFSV4ACE_APPENDDATA;
		perm |= KAUTH_VNODE_APPEND_DATA;
	}
	if (mask & NFSV4ACE_ADDSUBDIRECTORY) {
		mask &= ~NFSV4ACE_ADDSUBDIRECTORY;
		perm |= KAUTH_VNODE_ADD_SUBDIRECTORY;
	}
	if (mask & NFSV4ACE_READNAMEDATTR) {
		mask &= ~NFSV4ACE_READNAMEDATTR;
		perm |= KAUTH_VNODE_READ_EXTATTRIBUTES;
	}
	if (mask & NFSV4ACE_WRITENAMEDATTR) {
		mask &= ~NFSV4ACE_WRITENAMEDATTR;
		perm |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
	}
	if (mask & NFSV4ACE_EXECUTE) {
		mask &= ~NFSV4ACE_EXECUTE;
		perm |= KAUTH_VNODE_EXECUTE;
	}
	if (mask & NFSV4ACE_SEARCH) {
		mask &= ~NFSV4ACE_SEARCH;
		perm |= KAUTH_VNODE_SEARCH;
	}
	if (mask & NFSV4ACE_DELETECHILD) {
		mask &= ~NFSV4ACE_DELETECHILD;
		perm |= KAUTH_VNODE_DELETE_CHILD;
	}
	if (mask & NFSV4ACE_READATTRIBUTES) {
		mask &= ~NFSV4ACE_READATTRIBUTES;
		perm |= KAUTH_VNODE_READ_ATTRIBUTES;
	}
	if (mask & NFSV4ACE_WRITEATTRIBUTES) {
		mask &= ~NFSV4ACE_WRITEATTRIBUTES;
		perm |= KAUTH_VNODE_WRITE_ATTRIBUTES;
	}
	if (mask & NFSV4ACE_DELETE) {
		mask &= ~NFSV4ACE_DELETE;
		perm |= KAUTH_VNODE_DELETE;
	}
	if (mask & NFSV4ACE_READACL) {
		mask &= ~NFSV4ACE_READACL;
		perm |= KAUTH_VNODE_READ_SECURITY;
	}
	if (mask & NFSV4ACE_WRITEACL) {
		mask &= ~NFSV4ACE_WRITEACL;
		perm |= KAUTH_VNODE_WRITE_SECURITY;
	}
	if (mask & NFSV4ACE_WRITEOWNER) {
		mask &= ~NFSV4ACE_WRITEOWNER;
		perm |= KAUTH_VNODE_CHANGE_OWNER;
	}
	if (mask & NFSV4ACE_SYNCHRONIZE) {
		mask &= ~NFSV4ACE_SYNCHRONIZE;
		perm |= KAUTH_VNODE_SYNCHRONIZE;
	}
	if (mask != 0)
		return (NFSERR_ATTRNOTSUPP);
	*permp = perm;
	return (0);
}

/* local functions */
static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
    enum vtype, int, int, kauth_ace_t);

/*
 * This function builds an NFS ace.
 */
static int
nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
    enum vtype type, int group, int owner, kauth_ace_t ace)
{
	u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
	int full_len;

	full_len = NFSM_RNDUP(namelen);
	NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED + full_len);

	/*
	 * Fill in the ace type.
	 */
	if (ace->ace_flags & KAUTH_ACE_PERMIT)
		acetype = NFSV4ACE_ALLOWEDTYPE;
	else if (ace->ace_flags & KAUTH_ACE_DENY)
		acetype = NFSV4ACE_DENIEDTYPE;
	else if (ace->ace_flags & KAUTH_ACE_AUDIT)
		acetype = NFSV4ACE_AUDITTYPE;
	else
		acetype = NFSV4ACE_ALARMTYPE;
	*tl++ = txdr_unsigned(acetype);

	/*
	 * Set the flag bits from the ACL.
	 */
	if (ace->ace_flags & KAUTH_ACE_FILE_INHERIT)
		aceflag |= NFSV4ACE_FILEINHERIT;
	if (ace->ace_flags & KAUTH_ACE_DIRECTORY_INHERIT)
		aceflag |= NFSV4ACE_DIRECTORYINHERIT;
	if (ace->ace_flags & KAUTH_ACE_LIMIT_INHERIT)
		aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
	if (ace->ace_flags & KAUTH_ACE_ONLY_INHERIT)
		aceflag |= NFSV4ACE_INHERITONLY;
	if (ace->ace_flags & KAUTH_ACE_SUCCESS)
		aceflag |= NFSV4ACE_SUCCESSFULACCESS;
	if (ace->ace_flags & KAUTH_ACE_FAILURE)
		aceflag |= NFSV4ACE_FAILEDACCESS;
	if (group)
		aceflag |= NFSV4ACE_IDENTIFIERGROUP;
	*tl++ = txdr_unsigned(aceflag);
	if (type == VDIR) {
		if (ace->ace_rights & KAUTH_VNODE_LIST_DIRECTORY)
			acemask |= NFSV4ACE_LISTDIRECTORY;
		if (ace->ace_rights & KAUTH_VNODE_ADD_FILE)
			acemask |= NFSV4ACE_ADDFILE;
		if (ace->ace_rights & KAUTH_VNODE_ADD_SUBDIRECTORY)
			acemask |= NFSV4ACE_ADDSUBDIRECTORY;
		if (ace->ace_rights & KAUTH_VNODE_READ_EXTATTRIBUTES)
			acemask |= NFSV4ACE_READNAMEDATTR;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_EXTATTRIBUTES)
			acemask |= NFSV4ACE_WRITENAMEDATTR;
		if (ace->ace_rights & KAUTH_VNODE_SEARCH)
			acemask |= NFSV4ACE_SEARCH;
		if (ace->ace_rights & KAUTH_VNODE_DELETE_CHILD)
			acemask |= NFSV4ACE_DELETECHILD;
		if (ace->ace_rights & KAUTH_VNODE_READ_ATTRIBUTES)
			acemask |= NFSV4ACE_READATTRIBUTES;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_ATTRIBUTES)
			acemask |= NFSV4ACE_WRITEATTRIBUTES;
		if (ace->ace_rights & KAUTH_VNODE_DELETE)
			acemask |= NFSV4ACE_DELETE;
		if (ace->ace_rights & KAUTH_VNODE_READ_SECURITY)
			acemask |= NFSV4ACE_READACL;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_SECURITY)
			acemask |= NFSV4ACE_WRITEACL;
		if (ace->ace_rights & KAUTH_VNODE_CHANGE_OWNER)
			acemask |= NFSV4ACE_WRITEOWNER;
	} else {
		if (ace->ace_rights & KAUTH_VNODE_READ_DATA)
			acemask |= NFSV4ACE_READDATA;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_DATA)
			acemask |= NFSV4ACE_WRITEDATA;
		if (ace->ace_rights & KAUTH_VNODE_APPEND_DATA)
			acemask |= NFSV4ACE_APPENDDATA;
		if (ace->ace_rights & KAUTH_VNODE_READ_EXTATTRIBUTES)
			acemask |= NFSV4ACE_READNAMEDATTR;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_EXTATTRIBUTES)
			acemask |= NFSV4ACE_WRITENAMEDATTR;
		if (ace->ace_rights & KAUTH_VNODE_EXECUTE)
			acemask |= NFSV4ACE_EXECUTE;
		if (ace->ace_rights & KAUTH_VNODE_READ_ATTRIBUTES)
			acemask |= NFSV4ACE_READATTRIBUTES;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_ATTRIBUTES)
			acemask |= NFSV4ACE_WRITEATTRIBUTES;
		if (ace->ace_rights & KAUTH_VNODE_DELETE)
			acemask |= NFSV4ACE_DELETE;
		if (ace->ace_rights & KAUTH_VNODE_READ_SECURITY)
			acemask |= NFSV4ACE_READACL;
		if (ace->ace_rights & KAUTH_VNODE_WRITE_SECURITY)
			acemask |= NFSV4ACE_WRITEACL;
		if (ace->ace_rights & KAUTH_VNODE_CHANGE_OWNER)
			acemask |= NFSV4ACE_WRITEOWNER;
		if (ace->ace_rights & KAUTH_VNODE_SYNCHRONIZE)
			acemask |= NFSV4ACE_SYNCHRONIZE;
	}
	*tl++ = txdr_unsigned(acemask);
	*tl++ = txdr_unsigned(namelen);
	if (full_len - namelen)
		*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
	NFSBCOPY(name, (caddr_t)tl, namelen);
	return (full_len + 4 * NFSX_UNSIGNED);
}

/*
 * Build an NFSv4 ACL.
 */
APPLESTATIC int
nfsrv_buildacl(struct nfsrv_descript *nd, kauth_acl_t aclp, enum vtype type,
    NFSPROC_T *p)
{
	int i, entrycnt = 0, retlen, error, wkg;
	u_int32_t *entrycntp;
	int isowner, isgroup, namelen, malloced, acecnt;
	u_char *name, namestr[NFSV4_SMALLSTR];
	kauth_ace_t ace;
	uid_t uid;
	gid_t gid;

	NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
	retlen = NFSX_UNSIGNED;
	if (aclp != NULL)
		acecnt = aclp->acl_entrycount;
	else
		acecnt = 0;
	/*
	 * Loop through the acl entries, building each one.
	 */
	for (i = 0; i < acecnt; i++) {
		isowner = isgroup = malloced = 0;
		error = 0;
		ace = &aclp->acl_ace[i];
		wkg = kauth_wellknown_guid(&ace->ace_applicable);
		switch (wkg) {
		case KAUTH_WKG_OWNER:
			isowner = 1;
			name = "OWNER@";
			namelen = 6;
			break;
		case KAUTH_WKG_GROUP:
			isgroup = 1;
			name = "GROUP@";
			namelen = 6;
			break;
		case KAUTH_WKG_EVERYBODY:
			name = "EVERYONE@";
			namelen = 9;
			break;
		case KAUTH_WKG_NOT:
			/*
			 * XXX Yuck! There doesn't seem to be a way to
			 * determine whether a guid refers to a user or group.
			 * All I can think of is trying group first and then
			 * falling back to user. Can a guid refer to both a
			 * user and group? If so, this code is BUSTED!
			 */
			error = kauth_cred_guid2gid(&ace->ace_applicable,
			    &gid);
			if (error == 0) {
				isgroup = 1;
				name = namestr;
				nfsv4_gidtostr(gid, &name,
				    &namelen, p);
				if (name != namestr)
					malloced = 1;
			} else {
				error = kauth_cred_guid2uid(
				    &ace->ace_applicable, &uid);
				if (error == 0) {
					name = namestr;
					nfsv4_uidtostr(uid, &name,
					    &namelen, p);
					if (name != namestr)
						malloced = 1;
				}
			}
			break;
		default:
			continue;
		};
		if (error == 0) {
			retlen += nfsrv_buildace(nd, name, namelen, type,
			    isgroup, isowner, ace);
			entrycnt++;
		}
		if (malloced)
			free(name, M_NFSSTRING);
	}
	*entrycntp = txdr_unsigned(entrycnt);
	return (retlen);
}

/*
 * Compare two NFSv4 acls.
 * Return 0 if they are the same, 1 if not the same.
 */
APPLESTATIC int
nfsrv_compareacl(kauth_acl_t aclp1, kauth_acl_t aclp2)
{

	/* Not used by the client, so why bother... */
	return (1);
}

/*
 * Set the well known guid value. Essentially the inverse of
 * kauth_wellknown_guid().
 */
static void
nfsacl_set_wellknown(int wkg, guid_t *guid)
{
	static char	fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};

	/*
	 * All WKGs begin with the same 12 bytes.
	 */
	bcopy(fingerprint, (void *)guid, 12);
	/*
	 * The final 4 bytes are our code (in network byte order).
	 */
	switch (wkg) {
	case KAUTH_WKG_EVERYBODY:
		*((u_int32_t *)&guid->g_guid[12]) = OSSwapBigToHostInt32(0x0000000c);
		break;
	case KAUTH_WKG_NOBODY:
		*((u_int32_t *)&guid->g_guid[12]) = OSSwapBigToHostInt32(0xfffffffe);
		break;
	case KAUTH_WKG_OWNER:
		*((u_int32_t *)&guid->g_guid[12]) = OSSwapBigToHostInt32(0x0000000a);
		break;
	case KAUTH_WKG_GROUP:
		*((u_int32_t *)&guid->g_guid[12]) = OSSwapBigToHostInt32(0x00000010);
	};
}

/*
 * Fill in an ace with the given arguments.
 */
static void
nfsacl_fillace(kauth_ace_t ace, int wkg, kauth_ace_rights_t rights,
    u_int32_t flags)
{

	nfsacl_set_wellknown(wkg, &ace->ace_applicable);
	ace->ace_rights = rights;
	ace->ace_flags = flags;
}

/*
 * Create an acl with the 6 canonical entries created from the file's mode
 * bits.
 * Modified from acl_nfs4_sync_acl_from_mode(), written by Edward Tranz
 * Napieri for FreeBSD-CURRENT.
 */
APPLESTATIC kauth_acl_t
nfsacl_from_mode(u_short mode, kauth_acl_t oaclp)
{
	kauth_acl_t aclp;
	int start6, i;

	if (oaclp != NULL) {
		/*
		 * Allocate a new acl, so that the 6 canonical ACEs can be
		 * appended.
		 */
		aclp = kauth_acl_alloc(oaclp->acl_entrycount + 6);
		if (aclp != NULL) {
			start6 = oaclp->acl_entrycount;
			aclp->acl_entrycount = start6 + 6;
			aclp->acl_flags = oaclp->acl_flags;
			for (i = 0; i < start6; i++)
				aclp->acl_ace[i] = oaclp->acl_ace[i];
		}
	} else {
		aclp = kauth_acl_alloc(6);
		if (aclp != NULL) {
			start6 = 0;
			aclp->acl_entrycount = 6;
			aclp->acl_flags = 0;
		}
	}
	if (aclp != NULL) {
		/*
		 * First, set up the 6 basic aces.
		 */
		nfsacl_fillace(&aclp->acl_ace[start6], KAUTH_WKG_OWNER, 0, KAUTH_ACE_DENY);
		nfsacl_fillace(&aclp->acl_ace[start6 + 1], KAUTH_WKG_OWNER,
		    KAUTH_VNODE_WRITE_SECURITY |
		    KAUTH_VNODE_CHANGE_OWNER | KAUTH_VNODE_WRITE_ATTRIBUTES |
		    KAUTH_VNODE_WRITE_EXTATTRIBUTES, KAUTH_ACE_PERMIT);
		nfsacl_fillace(&aclp->acl_ace[start6 + 2], KAUTH_WKG_GROUP, 0,
		    KAUTH_ACE_DENY);
		nfsacl_fillace(&aclp->acl_ace[start6 + 3], KAUTH_WKG_GROUP, 0,
		    KAUTH_ACE_PERMIT);
		nfsacl_fillace(&aclp->acl_ace[start6 + 4], KAUTH_WKG_EVERYBODY,
		    KAUTH_VNODE_WRITE_SECURITY |
		    KAUTH_VNODE_CHANGE_OWNER | KAUTH_VNODE_WRITE_ATTRIBUTES |
		    KAUTH_VNODE_WRITE_EXTATTRIBUTES, KAUTH_ACE_DENY);
		nfsacl_fillace(&aclp->acl_ace[start6 + 5], KAUTH_WKG_EVERYBODY,
		    KAUTH_VNODE_READ_SECURITY |
		    KAUTH_VNODE_READ_ATTRIBUTES | KAUTH_VNODE_READ_EXTATTRIBUTES |
		    KAUTH_VNODE_SYNCHRONIZE, KAUTH_ACE_PERMIT);
	
		/*
		 * Now, adjust them based on the mode bits.
		 */
		if (mode & S_IRUSR)
			aclp->acl_ace[start6 + 1].ace_rights |= KAUTH_VNODE_READ_DATA;
		else
			aclp->acl_ace[start6].ace_rights |= KAUTH_VNODE_READ_DATA;
		if (mode & S_IWUSR)
			aclp->acl_ace[start6 + 1].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		else
			aclp->acl_ace[start6].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		if (mode & S_IXUSR)
			aclp->acl_ace[start6 + 1].ace_rights |= KAUTH_VNODE_EXECUTE;
		else
			aclp->acl_ace[start6].ace_rights |= KAUTH_VNODE_EXECUTE;
	
		if (mode & S_IRGRP)
			aclp->acl_ace[start6 + 3].ace_rights |= KAUTH_VNODE_READ_DATA;
		else
			aclp->acl_ace[start6 + 2].ace_rights |= KAUTH_VNODE_READ_DATA;
		if (mode & S_IWGRP)
			aclp->acl_ace[start6 + 3].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		else
			aclp->acl_ace[start6 + 2].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		if (mode & S_IXGRP)
			aclp->acl_ace[start6 + 3].ace_rights |= KAUTH_VNODE_EXECUTE;
		else
			aclp->acl_ace[start6 + 2].ace_rights |= KAUTH_VNODE_EXECUTE;
	
		if (mode & S_IROTH)
			aclp->acl_ace[start6 + 5].ace_rights |= KAUTH_VNODE_READ_DATA;
		else
			aclp->acl_ace[start6 + 4].ace_rights |= KAUTH_VNODE_READ_DATA;
		if (mode & S_IWOTH)
			aclp->acl_ace[start6 + 5].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		else
			aclp->acl_ace[start6 + 4].ace_rights |= (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA);
		if (mode & S_IXOTH)
			aclp->acl_ace[start6 + 5].ace_rights |= KAUTH_VNODE_EXECUTE;
		else
			aclp->acl_ace[start6 + 4].ace_rights |= KAUTH_VNODE_EXECUTE;
	}
	return (aclp);
}

#ifdef NFSD
/*
 * Check access for an NFSv4 acl.
 * The vflags are the basic VREAD, VWRITE, VEXEC. The mask is the NFSV4ACE
 * mask bits for the more detailed check.
 * If the more detailed check fails, due to no acl, do a basic one.
 */
APPLESTATIC int
nfsrv_aclaccess(vnode_t vp, accmode_t vflags, u_int32_t mask,
    struct ucred *cred, NFSPROC_T *p)
{
	int error = 0;
	accmode_t accmode;

	if (nfsrv_useacl == 0) {
		error = VOP_ACCESS(vp, vflags, cred, p);
		return (error);
	}

	/* Convert NFSV4ACE mask to accmode_t */
	accmode = 0;
	if (mask & NFSV4ACE_READDATA)
		accmode |= VREAD;
	if (mask & NFSV4ACE_LISTDIRECTORY)
		accmode |= VREAD;
	if (mask & NFSV4ACE_WRITEDATA)
		accmode |= VWRITE;
	if (mask & NFSV4ACE_ADDFILE)
		accmode |= VWRITE;
	if (mask & NFSV4ACE_APPENDDATA)
		accmode |= VAPPEND;
	if (mask & NFSV4ACE_ADDSUBDIRECTORY)
		accmode |= VAPPEND;
	if (mask & NFSV4ACE_READNAMEDATTR)
		accmode |= VREAD_NAMED_ATTRS;
	if (mask & NFSV4ACE_WRITENAMEDATTR)
		accmode |= VWRITE_NAMED_ATTRS;
	if (mask & NFSV4ACE_EXECUTE)
		accmode |= VEXEC;
	if (mask & NFSV4ACE_SEARCH)
		accmode |= VEXEC;
	if (mask & NFSV4ACE_DELETECHILD)
		accmode |= VDELETE_CHILD;
	if (mask & NFSV4ACE_READATTRIBUTES)
		accmode |= VREAD_ATTRIBUTES;
	if (mask & NFSV4ACE_WRITEATTRIBUTES)
		accmode |= VWRITE_ATTRIBUTES;
	if (mask & NFSV4ACE_DELETE)
		accmode |= VDELETE;
	if (mask & NFSV4ACE_READACL)
		accmode |= VREAD_ACL;
	if (mask & NFSV4ACE_WRITEACL)
		accmode |= VWRITE_ACL;
	if (mask & NFSV4ACE_WRITEOWNER)
		accmode |= VWRITE_OWNER;
	if (mask & NFSV4ACE_SYNCHRONIZE)
		accmode |= VSYNCHRONIZE;

	if (accmode != 0)
		error = VOP_ACCESS(vp, accmode, cred, p);
	else
		error = VOP_ACCESS(vp, vflags, cred, p);
	return (error);
}

/*
 * Set an NFSv4 acl.
 */
APPLESTATIC int
nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
    NFSPROC_T *p)
{
	int error;

	if (nfsrv_useacl == 0 || !NFSHASNFS4ACL(vnode_mount(vp)))
		return (NFSERR_ATTRNOTSUPP);
	/*
	 * With NFS4 ACLs, chmod(2) may need to add additional entries.
	 * Make sure it has enough room for that - splitting every entry
	 * into two and appending "canonical six" entries at the end.
	 * Cribbed out of kern/vfs_acl.c - Rick M.
	 */
	if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2)
		return (NFSERR_ATTRNOTSUPP);
	error = VOP_ACLCHECK(vp, ACL_TYPE_NFS4, aclp, cred, p);
#ifdef MAC
	if (!error)
		error = mac_check_vnode_setacl(cred, vp, ACL_TYPE_NFS4, aclp);
#endif
	if (!error)
		error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
	return (error);
}

#endif	/* NFSD */
